:- module(instprofile).
:- export statsample / 5.
:- export aggregate_result / 2.
:- export reset_body / 1.
:- export result_body / 1.
:- export statprofile_body / 4.
:- export statprofile_body / 3.
:- export profile_result / 5.
:- export close_delta_file / 0.
:- export open_delta_file / 1.
:- export statsample_control / 2.
:- export statsample / 4.
:- export reset / 0.
:- export aggregate_result / 1.
:- export result / 0.
:- export statprofile / 3.
:- export statprofile / 2.
:- comment(summary, "Instrumentation / sampling based statistics profiler").
:- comment(author, "Andrew Cheadle").
:- comment(date, "$Date: 2004/11/17 14:06:53 $").
:- comment(copyright, "Imperial College London and Parc Technologies Ltd").
:- comment(status, prototype).
:- comment(desc, html("<P>\n   The instprofile library provides two complimentary mechanisms for\n   the profiling of ECLiPSe code with the metrics provided by the\n   statistics/2 predicate:\n   <OL>\n   <LI>Instrumentation based, where metric measuring code is\n   inserted directly into the user program in order to accurately\n   determine how its constituent pieces of code perform. The\n   mechanism for the insertion of instrumentation code is based upon \n   the facilities of the instrument library. As such, the statistic \n   measurements may be inserted at the beginning and end of clause or block \n   definitions, and around subgoals and/or predicate calls.  \n   <LI>Sample driven, where metric measuring code is executed at a user\n   defined interval as the user program runs. The sample based profiler is \n   implemented using after events and is therefore less intrusive in its\n   effect on the performance of the executing code. \n   </OL>\n   </P><P>\n   While the performance overhead of the sampling profiler is lower, it \n   cannot be used to determine the exact cost of a piece of code for a \n   particular statistic. It merely provides snapshots with respect to time \n   not code location. The instrumentation based profiler does however\n   provide the means for the measurement of both the aggregated cost for \n   all executions of a piece of code and also the cost for each individual \n   execution.\n   </P><P>\n   The usage of the instrumentation based profiler is as follows:\n   <OL>\n   <LI>Load the instprofile library\n   <PRE>\n   ?- lib(instprofile).\n   </PRE>\n   <LI>Compile your program with instrumentation profiling:\n   <PRE>\n   ?- instprofile:statprofile(my_program, [global_stack_used,\n                              trail_stack_used]).\n   </PRE>\n   <LI>Run the query for which you wish to generate mode analysis data\n   <PRE>\n   ?- my_query(X,Y,Z).\n   </PRE>\n   <LI>Generate the results for the module into which the program\n       was compiled.\n   <PRE>\n   ?- instprofile:result@my_program_module.\n   </PRE>\n   </OL>\n   The usage of the sampling profiler is as follows:\n   <OL>\n   <LI>Load the instprofile library\n   <PRE>\n   ?- lib(instprofile).\n   </PRE>\n   <LI>Define a sampling profile\n   <PRE>\n   ?- instprofile:statsample(memory, 5, [global_stack_used,\n                             trail_stack_used], 'memory_sample.dat').\n   </PRE>\n   <LI>Prior to running the queries for which you wish to gather \n   sampling data, enable the sampling profiler for the named profile\n   <PRE>\n   ?- instprofile:statsample_control(memory, on)\n   </PRE>\n   <LI>Run the query for which you wish to generate sampling data\n   <PRE>\n   ?- my_query(X,Y,Z).\n   </PRE>\n   <LI>Disable the sampling profiler for the named profile. The results\n   file specified in statsample/4 contains the sampling data.    \n   <PRE>\n   ?- instprofile:statsample_control(memory, off)\n   </PRE>\n   </OL>\n   </P><P>\n   Limitations to be resolved shortly: \n   <OL>\n   <LI><P>\n   Results are currently produced per module, not for individual files\n   within a module (i.e. results must be retrieved for all files within \n   a module).\n   </P>\n   <LI><P>When multiple statistics are specified, the order in which the\n   statistics will appear in the results (pretty-printed source and results\n   file) is displayed during the invocation of the statsample predicate,\n   not the result predicate.\n   </P>\n   <LI><P>Results are produced to a file or annotated on pretty-printed\n   source and these results to be viewed offline. Additional facilities \n   are to be provided that allow for the real time visualisation of results.\n   </P>\n   <LI><P>The model that accounts for the cost of inserted instrumentation is \n   fairly rudimentary (especially for the 'global_stack_used' statistic.\n   </P>\n   <LI>A future enhancement will enable the insertion of user defined metrics\n   not just those available via statistics/2.\n   </OL>\n   </P>")).
:- comment(aggregate_result / 1, [summary : "Write the aggregated instrument profiling results to\n   a named file.", args : ["File" : "Atom or string"], amode : aggregate_result(+), resat : no, fail_if : "The calling context module has not been profiled.", exceptions : [4 : "File is not instantiated", 5 : "File is not an atom or string"], see_also : [statprofile / 2, statprofile / 3, aggregate_result / 1, open_delta_file / 1, close_delta_file / 0, reset / 0, statistics / 2, library(instprofile)], desc : html("<P>    \n   This will write the aggregated results of instrumentation profiling \n   for all source files that have been compiled into the calling module \n   context and profiled with the instrument profiler.\n   </P><P>\n   NOTE: Memory related statistics are displayed in kilobytes not bytes.\n   </P><P>\n   This predicate should be executed before result/0 predicate, since\n   result/0 also invokes reset/0 which resets the statistics.\n   </P><P>\n   The use of this predicate is only meaningful if the module has\n   previously been compiled for instrumentation profiling using \n   <TT>instprofile:statprofile/2,3</TT>, and the code has been\n   run in order to obtain profiling results.\n   </P><P>\n   The predicate is a tool and the profiling results of a\n   module other than the current calling module context can be obtained\n   by invoking <TT>aggregate_result@Module</TT>.\n   </P>")]).
:- comment(close_delta_file / 0, [summary : "Close the instrumentation profiler's delta results file.", args : [], resat : no, fail_if : no, see_also : [open_delta_file / 1, statprofile / 3, statistics / 2, library(instprofile)], desc : html("<P>    \n   Close the file that the instrumentation profiler writes delta values to.\n   The file must previously have been opened using open_delta_file/1. \n   </P><P>\n   Delta values are the instrumentation profiling results for the single\n   execution (as opposed to aggregated) of a profiled code fragment.\n   </P><P>\n   For performance reasons, the results are written to the file using \n   buffered I/O - each result is not flushed to the file as it is emitted. \n   As a result to ensure all results are flushed to the disk file,\n   <TT>close_delta_file</TT> must be executed.\n   </P>")]).
:- comment(open_delta_file / 1, [summary : "Open the instrumentation profiler's delta results file.", args : ["DeltaFile" : "Atom or string"], amode : open_delta_file(+), resat : no, fail_if : no, exceptions : [4 : "File is not instantiated", 5 : "File is not an atom or string"], see_also : [close_delta_file / 0, statprofile / 3, statistics / 2, library(instprofile)], desc : html("<P>    \n   Open the file that the instrumentation profiler writes delta values to.\n   If a file has been previously opened using open_delta_file/1 it is shut\n   before the specified file is opened. \n   </P><P>\n   Delta values are the instrumentation profiling results for the single\n   execution (as opposed to aggregated) of a profiled code fragment.\n   </P><P>\n   NOTE: Memory related statistics are displayed in bytes.\n   </P><P>\n   For performance reasons, the results are written to the file using \n   buffered I/O - each result is not flushed to the file as it is emitted. \n   As a result to ensure all results are flushed to the disk file,\n   <TT>close_delta_file</TT> must be executed.\n   </P>")]).
:- comment(profile_result / 5, hidden).
:- comment(reset / 0, [summary : "Reset the profiling statistics for the calling context\n   module.", args : [], resat : no, fail_if : "The calling context module has not been profiled.", see_also : [statprofile / 2, statprofile / 3, open_delta_file / 1, close_delta_file / 0, result / 0, aggregate_result / 1, statistics / 2, library(instprofile)], desc : html("<P>    \n   This will reset the currently stored aggregated profiling results for \n   the calling context module.\n   </P><P>\n   The use of this predicate is only meaningful if the module has\n   previously been compiled for instrumentation profiling using \n   <TT>instprofile:statprofile/2,3</TT>, and the code has been\n   run in order to obtain profiling results.\n   </P><P>\n   The predicate is a tool and the profiling results of a\n   module other than the current calling module context can be obtained\n   by invoking <TT>reset@Module</TT>.\n   </P>")]).
:- comment(result / 0, [summary : "Pretty-print the instrument profiled source with\n   aggregated results.", args : [], resat : no, fail_if : "The calling context module has not been profiled.", see_also : [statprofile / 2, statprofile / 3, aggregate_result / 1, open_delta_file / 1, close_delta_file / 0, reset / 0, statistics / 2, library(instprofile)], desc : html("<P>    \n   This will pretty-print all source files in the calling module context,\n   annotated with the aggregated results of instrumentation profiling.  \n   The resulting .html files are placed in a sub-directory called\n   'instprofile', relative to the files that were compiled into the file.\n   </P><P>\n   NOTE: Memory related statistics are displayed in kilobytes not bytes.\n   </P><P>\n   The use of this predicate is only meaningful if the module has\n   previously been compiled for instrumentation profiling using \n   <TT>instprofile:statprofile/2,3</TT>, and the code has been\n   run in order to obtain profiling results.\n   </P><P>\n   The predicate is a tool and the profiling results of a\n   module other than the current calling module context can be obtained\n   by invoking <TT>result@Module</TT>.\n   </P>")]).
:- comment(statprofile / 2, [summary : "Instrument a file with statistics profiling code.", args : ["File" : "Atom or string", "Statistics" : "List of statistic keywords"], amode : statprofile(+, +), resat : no, fail_if : "Statistics is empty or contains an invalid statistic/2 keyword.", exceptions : [5 : "File is not an atom or string"], see_also : [statprofile / 3, statistics / 2, result / 0, aggregate_result / 1, reset / 0, open_delta_file / 1, close_delta_file / 0, library(instprofile), library(instrument)], desc : html("<P>    \n   The predicate inserts into the user code of File, instrumentation \n   for the collection of the statistics (statistics/2 metric keywords) \n   specified in Statistics. The instrumentation is inserted at clause \n   entry and exit points (i.e. the start, end, fail and redo ports of \n   the conceptual predicate box model) and aggregates the cost for each \n   of the metrics as the program executes. The source code can be annotated \n   with these aggregated results using the result/0 predicate. Additionally, \n   the results may be dumped to a file in a format suitable for offline \n   analysis and graphing.\n   </P><P>\n   statprofile/3 should be used for instrumentation profiling of code at a\n   finer granularity than the clause definition. statprofile/3 provides \n   options to prevent the profiling of recursive predicates. It also, \n   provides an option to produce the results for a single execution of a \n   profiled piece of code, in addition to the aggregated results.\n   </P><P>\n   The instrumentation profiler places an overhead on the execution of the \n   code undergoing profiling due to the insertion of the profiling code.\n   The sample based profiler is implemented using after events and is\n   therefore less intrusive in its effect on the performance of the \n   executing code than the instrumentation profiler. However, it is used to\n   indicate the trend of resource usage over time of a running program not to\n   attribute specific costs of a statistic to a specific piece of code. The\n   instrumentation based profiler is used to do this.\n   </P>"), eg : "\n   [eclipse 1]: instprofile:statprofile(queen, [global_stack_used, trail_stack_used]).\n\n   Yes (0.06s cpu)\n   "]).
:- comment(statprofile / 3, [summary : "Instrument a file with statistics profiling code.", args : ["File" : "Atom or string", "Statistics" : "List of statistic keywords", "OptionsList" : "List of Name:Value pairs"], amode : statprofile(+, +, +), resat : no, fail_if : "Statistics is empty or contains an invalid statistic/2 keyword.", exceptions : [5 : "File is not an atom or string."], see_also : [open_delta_file / 1, close_delta_file / 0, result / 0, aggregate_result / 1, reset / 0, statprofile / 2, statistics / 2, library(instprofile), library(instrument), instrument / 3], desc : html("<P>    \n   The predicate inserts into the user code of File, instrumentation \n   for the collection of the statistics (statistics/2 metric keywords) \n   specified in Statistics. The instrumentation is inserted by default \n   at clause entry and exit points (i.e. the start, end, fail and redo \n   ports of the conceptual predicate box model) and aggregates the cost \n   for each of the metrics as the program executes. The source code can \n   be annotated with these aggregated results using the result/0 predicate. \n   Additionally, the results may be dumped to a file in a format suitable \n   for offline analysis and graphing.\n   </P><P>\n   OptionList may contain the following options:\n   <DL><DT>\n   <TT>instrument_style</TT>  (default:<TT>clause</TT>)\n   <DD>\n   Valid values for this option are <TT>clause</TT>, <TT>block</TT>, \n   <TT>subgoal</TT> and <TT>call</TT>. These values correspond to the\n   instrument insertion points of instrument library. Each provides a\n   different level of granularity for the profiling of code fragments.\n   <DT>\n   <TT>delta_results</TT> (default:<TT>off</TT>)\n   <DD>\n   Specifying this option as <TT>on</TT> indicates that the instrumentation\n   results for execution of individual profiled code fragments (associated by\n   instrument's callsite identifier) should be written to the file specified \n   using open_delta_file/1. More specifically, these are the delta results,\n   the difference for each of the statistics between the start and end of the\n   profiled code fragment's execution.\n   <DT>\n   Instrument library options\n   <DD>\n   In addition to the above options, options valid to the instrument\n   library can also be passed in OptionsList. The most useful of which are \n   <TT>verbose</TT> and <TT>instrument_recursive</TT>. \n   <TT>instrument_recursive</TT> is used to prevent the incorrect aggregation\n   of results accumulated during the execution of recursive predicates, by\n   default it is off, i.e. recursive predicates will not be instrumented.\n   </DL>\n   </P><P>\n   The instrumentation profiler places an overhead on the execution of the \n   code undergoing profiling due to the insertion of the profiling code.\n   The sample based profiler is implemented using after events and is\n   therefore less intrusive in its effect on the performance of the \n   executing code than the instrumentation profiler. However, it is used to\n   indicate the trend of resource usage over time of a running program not to\n   attribute specific costs of a statistic to a specific piece of code. The\n   instrumentation based profiler is used to do this.\n   </P>"), eg : "\n   [eclipse 1]: instprofile:statprofile(queen,[global_stack_used, trail_stack_used], \n                                        [instrument_style:block, delta_results:on]).\n\n   Yes (0.06s cpu)\n   "]).
:- comment(statsample / 4, [summary : "Create a statistics sampling profile.", args : ["ProfileName" : "Atom or string", "SamplePeriod" : "Number", "Statistics" : "List of statistic keywords", "File" : "Atom or string"], amode : statsample(+, +, +, +), resat : no, fail_if : "Statistics is empty or contains an invalid statistic/2 keyword.", exceptions : [5 : "File is not an atom or string"], see_also : [statsample_control / 2, statistics / 2, library(instprofile)], desc : html("<P>    \n   The predicate defines a profile, ProfileName, for use by the sampling\n   profiler. Samples of the statistics (statistics/2 metric keywords)\n   specified in Statistics are collected every SamplePeriod seconds and \n   stored in File. The format of file is such that it can easily be\n   analysed or graphed offline.\n   </P><P>\n   The sample based profiler is implemented using after events and is\n   therefore less intrusive in its effect on the performance of the \n   executing code than the instrumentation profiler. However, it is used to\n   indicate the trend of resource usage over time of a running program not to\n   attribute specific costs of a statistic to a specific piece of code. The\n   instrumentation based profiler is used to do this.\n   </P>"), eg : "\n   [eclipse 1]: instprofile:statsample(memory, 5, [global_stack_used,\n                                       trail_stack_used], 'memory_sample.dat').\n   string     compiled traceable 476 bytes in 0.00 seconds\n\n   Yes (0.01s cpu)\n   "]).
:- comment(statsample_control / 2, [summary : "Enable / disable the sampling profiler for a named profile.", args : ["ProfileName" : "Atom or string", "State" : "Atoms 'on' or 'off'"], amode : statsample_control(+, +), resat : no, fail_if : "Profilename is not a valid profile defined by statsample/4.State is neither the atom 'on' nor the atom 'off'.", see_also : [statsample / 4, statistics / 2, library(instprofile)], desc : html("<P>    \n   For the named profile, ProfileName, enable or disable the sampling\n   profiler based on the value of State. Setting State to 'on' immediately\n   enables sample collection at the inteval and to to the file specified in\n   statsample/4.\n   </P><P>\n   For performance reasons, the results are written to File (specified by\n   statsample/4) using buffered I/O - each result is not flushed to disk as\n   it is written. As a result to ensure all results are flushed to the file,\n   <TT>statsample_control(ProfileName, off)</TT> must be executed before\n   the results file can be analysed.\n   </P>"), eg : "\n   [eclipse 2]: instprofile:statsample_control(memory, on).\n\n   Yes (0.00s cpu)\n   [eclipse 3]: ...\n\n   Yes (10.24s cpu)\n   [eclipse 4]: instprofile:statsample_control(memory, off).\n\n   Yes (0.00s cpu)\n        "]).
:- comment(statsample / 5, hidden).
:- comment(aggregate_result / 2, hidden).
:- comment(reset_body / 1, hidden).
:- comment(result_body / 1, hidden).
:- comment(statprofile_body / 4, hidden).
:- comment(statprofile_body / 3, hidden).
